﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Serialization;
using System.IO;

namespace BSGen
{
    class CodeGenerator
    {
        private Assembly sourceAssembly = null;
        private List<Type> targetTypeList;
        private List<Assembly> loadedAssemblies;

        /// <summary>
        /// Start the code generation procedure
        /// </summary>
        public void GenerateCode(string inputAssembly, string outputFolder)
        {
            //create the lists used to detect the types
            this.loadedAssemblies = new List<Assembly>();
            this.targetTypeList = new List<Type>();

            //load the assembly
            this.sourceAssembly = this.loadAssemblyAndDependences(inputAssembly);

            //create a list with the types that will require BoostSerialize
            Type[] assemblyType = this.sourceAssembly.GetTypes();
            for (int i = 0; i < assemblyType.Length; i++)
            {
                //if (i == 248) i = i;
                if (needBoostSerializer(assemblyType[i]))
                {
                    targetTypeList.Add(assemblyType[i]);
                }
            }

            //generate a specialized class for each type
            for (int i = 0; i < this.targetTypeList.Count; i++)
            {
                //if (i == 43) i = i;
                this.generateSerializeClass(this.targetTypeList[i], outputFolder);
            }

        }

        #region "Detecting types on the input assembly"

        /// <summary>
        /// Load and assembly and all it's DLL dependecies
        /// </summary>
        private Assembly loadAssemblyAndDependences(string assemblyPath)
        {
            //load the assembly
            Assembly asm = Assembly.LoadFrom(assemblyPath);

            //referenced assemblies
            AssemblyName[] refAsm = asm.GetReferencedAssemblies();
            for (int i = 0; i < refAsm.Length; i++)
            {
                //search in the list
                bool alreadyLoaded = false;
                for (int j = 0; j < this.loadedAssemblies.Count; j++)
                {
                    if (this.loadedAssemblies[j].FullName == refAsm[i].FullName)
                    {
                        alreadyLoaded = true;
                        break;
                    }
                }

                if (!alreadyLoaded)
                {
                    //compose the name of the file assembly
                    string asmName = refAsm[i].Name;
                    System.IO.FileInfo fi = new System.IO.FileInfo(assemblyPath);
                    string refDllPath = System.IO.Path.Combine(fi.DirectoryName, asmName) + ".dll";
                    string refExePath = System.IO.Path.Combine(fi.DirectoryName, asmName) + ".exe";

                    //exists the exe or dll file in the target folder?
                    string refPath = "";
                    if (System.IO.File.Exists(refDllPath)) refPath = refDllPath;
                    if (System.IO.File.Exists(refExePath)) refPath = refExePath;

                    //recursivelly load the assembly
                    if (refPath != "")
                    {
                        Assembly loadedRefAsm = this.loadAssemblyAndDependences(refPath);
                        if (loadedRefAsm != null)
                        {
                            this.loadedAssemblies.Add(loadedRefAsm);
                        }
                    }
                }
            }

            //add the asm itselft to the list
            this.loadedAssemblies.Add(asm);

            //return loaded asm
            return asm;
        }

        /// <summary>
        /// Detects if a type requires BoostSerializer
        /// </summary>
        public bool needBoostSerializer(Type t)
        {
            bool retVal = false;

            //structs won't have specialized serializers
            if (t.IsValueType) return false;

            //abstract type won't require serializers
            if (t.IsAbstract) return false;

            //classes with no public constructor won'r require serializers
            ConstructorInfo consInfo = t.GetConstructor(Type.EmptyTypes);
            if ((consInfo != null) && !consInfo.IsPublic) return false;

            if (this.hasSerializableMembers(t))
            {
                retVal = true;
            }
            else
            {
                //check parent
                Type baseType = t.BaseType;
                if (baseType != typeof(object))
                {
                    if (baseType.IsClass)
                    {
                        retVal = this.hasSerializableMembers(baseType);
                    }
                }
            }

            return retVal;
        }

        /// <summary>
        /// Detects if a given type has any member property signed with the
        /// XmlElement or XmlAttribute attributes
        /// </summary>
        public bool hasSerializableMembers(Type t)
        {
            bool retVal = false;

            //debug
            if (t.Name.EndsWith("Map") || t.Name.EndsWith("Room")) retVal = false;

            //check fields
            FieldInfo[] fields = t.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo currMember = fields[i];
                object[] elementAtt = currMember.GetCustomAttributes(typeof(XmlElementAttribute), false);
                object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                object[] textAtt = currMember.GetCustomAttributes(typeof(XmlTextAttribute), false);
                if ((elementAtt.Length > 0) || (attributeAtt.Length > 0) || (textAtt.Length >0))
                {
                    retVal = true;
                    break;
                }
            }

            //check properties
            PropertyInfo[] properties = t.GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {
                PropertyInfo currMember = properties[i];
                object[] elementAtt = currMember.GetCustomAttributes(typeof(XmlElementAttribute), false);
                object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                object[] textAtt = currMember.GetCustomAttributes(typeof(XmlTextAttribute), false);
                if ((elementAtt.Length > 0) || (attributeAtt.Length > 0) || (textAtt.Length > 0))
                {
                    retVal = true;
                    break;
                }
            }

            return retVal;
        }

        #endregion

        #region "Generate the serialization code"

        /// <summary>
        /// Generates the source file of a Boost Serializer for the given type
        /// </summary>
        private void generateSerializeClass(Type type, string outputFolder)
        {
            //intermediate variables
            string fullyQTName = fullTypeName(type);
            string shortTName = type.Name;
            string className = type.FullName.Replace(".", "_");
            className = className.Replace("+", "_");

            //create the output stream
            FileStream outputFile = this.createOutputFile(type, outputFolder);

            //write the "usings" block
            this.fileWriteUsings(type, outputFile);

            //write the namespace and class declaration
            this.fileWriteClassOpen(outputFile, className);

            //write the basic methods
            this.fileWriteBasicMethods(outputFile, fullyQTName, shortTName);

            //write the operture of Serialize Inner method
            this.fileWriteSerializeInnerOpen(outputFile, fullyQTName);

            //write the serialization of attributes, recursivelly for parent types
            this.fileWriteSerializeTypeAttributes(outputFile, "objS", fullyQTName, shortTName, type);

            //write the serialization of elements, recursivelly for parent types
            this.fileWriteSerializeTypeElements(outputFile, "objS", fullyQTName, shortTName, type);

            //write the closure of the Serialize Inner method
            this.fileWriteSerializeInnerClosing(outputFile);

            //write the operture of the Deserialize Inner method
            this.fileWriteDeserializeInnerOpen(outputFile, fullyQTName);

            //write the reading of the attributes
            this.fileWriteDeserializeTypeAttributes(true, outputFile, "objS", fullyQTName, shortTName, type);

            //write the reading of the elements
            this.fileWriteDeserializeTypeElements(outputFile, "objS", fullyQTName, shortTName, type);

            //write the closure of the Deserialize Inner method
            this.fileWriteDeserializeInnerClosing(outputFile);

            //write the closing brackets
            this.fileWriteClassClose(outputFile);

            //finish the file
            outputFile.Flush();
            outputFile.Close();
        }

        #region "Serialize"

        /// <summary>
        /// Write the opening of the Serialize Inner method
        /// </summary>
        private void fileWriteSerializeInnerOpen(FileStream stream, string fullyQTName)
        {
            string template =
@"
        public override void SerializeInner(object obj2Serialize, System.Xml.XmlWriter xmlW)
        {
            //cast to concrete type
            {0} objS = obj2Serialize as {0};
";

            string data = template;
            data = data.Replace("{0}", fullyQTName);

            this.fileWriteString(stream, data);
        }

        /// <summary>
        /// Write the closing of the Serialize Inner method
        /// </summary>
        private void fileWriteSerializeInnerClosing(FileStream stream)
        {
            string template =
@"
        }
";
            string data = template;
            this.fileWriteString(stream, data);
        }

        #region "Write SerializeInner Attributes"

        private void fileWriteSerializeTypeAttributes(FileStream stream, string objPrefix, string fullyQTName, string shortTName, Type type)
        {
            //recursive call for base type
            if (type.BaseType != typeof(object))
            {
                this.fileWriteSerializeTypeAttributes(stream, objPrefix, fullyQTName, shortTName, type.BaseType);
            }

            //variables for [XmlText()] attribute
            PropertyInfo piTextProp = null;
            FieldInfo piTextField = null;

            //detect the members that require serialization
            List<FieldInfo> serializableFields = new List<FieldInfo>();
            List<XmlAttributeAttribute> serializableAttributes = new List<XmlAttributeAttribute>();
            FieldInfo[] fields = type.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo currMember = fields[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableFields.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlAttributeAttribute);
                    }

                    object[] textAttributeAtt = currMember.GetCustomAttributes(typeof(XmlTextAttribute), false);
                    if (textAttributeAtt.Length > 0)
                    {
                        piTextField = currMember;
                    }
                }
            }

            List<PropertyInfo> serializableProperties = new List<PropertyInfo>();
            PropertyInfo[] properties = type.GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {
                PropertyInfo currMember = properties[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableProperties.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlAttributeAttribute);
                    }

                    object[] textAttributeAtt = currMember.GetCustomAttributes(typeof(XmlTextAttribute), false);
                    if (textAttributeAtt.Length > 0)
                    {
                        piTextProp = currMember;
                    }
                }
            }

            if ((serializableFields.Count > 0) || (serializableProperties.Count > 0)
                || (piTextField != null) || (piTextProp != null))
            {
                //write header
                this.fileWriteSerializeTypeAttributesOpen(stream, type);

                //write fields that map to attribtues
                if (serializableFields.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableFields.Count; i++)
                    {
                        FieldInfo currField = serializableFields[i];
                        XmlAttributeAttribute currAtt = serializableAttributes[i];

                        if (currField.FieldType == typeof(int))
                        {
                            this.fileWriteSerializeIntAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(float))
                        {
                            this.fileWriteSerializeFloatAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(string))
                        {
                            this.fileWriteSerializeStringAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(bool))
                        {
                            this.fileWriteSerializeBoolAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(byte))
                        {
                            this.fileWriteSerializeByteAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType.IsEnum)
                        {
                            this.fileWriteSerializeEnumAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name, fullTypeName(currField.FieldType));
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }

                //write properties that match to attributes
                if (serializableProperties.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableProperties.Count; i++)
                    {
                        PropertyInfo currField = serializableProperties[i];
                        XmlAttributeAttribute currAtt = serializableAttributes[serializableFields.Count + i];

                        if (currField.PropertyType == typeof(int))
                        {
                            this.fileWriteSerializeIntAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(float))
                        {
                            this.fileWriteSerializeFloatAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(string))
                        {
                            this.fileWriteSerializeStringAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(bool))
                        {
                            this.fileWriteSerializeBoolAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(byte))
                        {
                            this.fileWriteSerializeByteAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType.IsEnum)
                        {
                            this.fileWriteSerializeEnumAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name, fullTypeName(currField.PropertyType));
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }

                //write field that match as "XmlText"
                if (piTextField != null)
                {
                    this.fileWriteSerializeStringAttributeField(stream, objPrefix, "text", piTextField.Name);
                }

                //write property that match as "XmlText"
                if (piTextProp != null)
                {
                    this.fileWriteSerializeStringAttributeField(stream, objPrefix, "text", piTextProp.Name);
                }

            }


        }

        private void fileWriteSerializeIntAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {1} [Int]
            this.writeIntAttribute(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeByteAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Byte]
            this.writeByteAttribute(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeFloatAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Float]
            this.writeFloatAttribute(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeBoolAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Bool]
            this.writeBoolAttribute(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeStringAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [String]
            this.writeStringAttribute(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeEnumAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName, string enumTypeName)
        {
            string template =
@"
            // Field name: {2} [Enum]
            this.writeEnumAttribute(xmlW, ""{1}"", typeof({3}), {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);
            data = data.Replace("{3}", enumTypeName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeTypeAttributesOpen(FileStream stream, Type type)
        {
            string template =
@"
            //
            // Atributtes for {0}
            //
";
            string data = template;
            data = data.Replace("{0}", type.Name);
            this.fileWriteString(stream, data);
        }

        #endregion

        #region "Write SerializeInner Elements"

        private void fileWriteSerializeTypeElements(FileStream stream, string objPrefix, string fullyQTName, string shortTName, Type type)
        {
            //recursive call for base type
            if (type.BaseType != typeof(object))
            {
                this.fileWriteSerializeTypeElements(stream, objPrefix, fullyQTName, shortTName, type.BaseType);
            }

            //detect the members that require serialization
            List<FieldInfo> serializableFields = new List<FieldInfo>();
            List<XmlElementAttribute> serializableAttributes = new List<XmlElementAttribute>();
            FieldInfo[] fields = type.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo currMember = fields[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlElementAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableFields.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlElementAttribute);
                    }
                }
            }

            List<PropertyInfo> serializableProperties = new List<PropertyInfo>();
            PropertyInfo[] properties = type.GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {
                PropertyInfo currMember = properties[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlElementAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableProperties.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlElementAttribute);
                    }
                }
            }
            
            if ((serializableFields.Count > 0) || (serializableProperties.Count > 0))
            {
                //write header
                this.fileWriteSerializeTypeElementsOpen(stream, type);

                if (serializableFields.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableFields.Count; i++)
                    {
                        FieldInfo currField = serializableFields[i];
                        XmlElementAttribute currAtt = serializableAttributes[i];

                        if (currField.FieldType.GetInterface("System.Collections.IList") != null)
                        {
                            this.fileWriteSerializeListElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(int))
                        {
                            this.fileWriteSerializeIntElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(float))
                        {
                            this.fileWriteSerializeFloatElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(string))
                        {
                            this.fileWriteSerializeStringElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(bool))
                        {
                            this.fileWriteSerializeBoolElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(byte))
                        {
                            this.fileWriteSerializeByteElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.FieldType.IsEnum)
                        {
                            this.fileWriteSerializeEnumElementField(stream, objPrefix, currAtt.ElementName, currField.Name, fullTypeName(currField.FieldType));
                        }
                        else if (currField.FieldType.IsValueType)
                        {
                            //struct!
                            this.fileWriteSerializeStructElement(stream, objPrefix, currAtt.ElementName, currField.Name, currField.FieldType);
                        }
                        else if (currField.FieldType.IsClass)
                        {
                            this.fileWriteSerializeClassElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }

                if (serializableProperties.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableProperties.Count; i++)
                    {
                        PropertyInfo currField = serializableProperties[i];
                        XmlElementAttribute currAtt = serializableAttributes[serializableFields.Count + i];

                        if (currField.PropertyType.GetInterface("System.Collections.IList") != null)
                        {
                            this.fileWriteSerializeListElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(int))
                        {
                            this.fileWriteSerializeIntElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(float))
                        {
                            this.fileWriteSerializeFloatElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(string))
                        {
                            this.fileWriteSerializeStringElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(bool))
                        {
                            this.fileWriteSerializeBoolElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(byte))
                        {
                            this.fileWriteSerializeByteElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else if (currField.PropertyType.IsEnum)
                        {
                            this.fileWriteSerializeEnumElementField(stream, objPrefix, currAtt.ElementName, currField.Name, fullTypeName(currField.PropertyType));
                        }
                        else if (currField.PropertyType.IsValueType)
                        {
                            //struct!
                            this.fileWriteSerializeStructElement(stream, objPrefix, currAtt.ElementName, currField.Name, currField.PropertyType);
                        }
                        else if (currField.PropertyType.IsClass)
                        {
                            this.fileWriteSerializeClassElementField(stream, objPrefix, currAtt.ElementName, currField.Name);
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }
            }

        }

        #region "Structs"

        private void fileWriteSerializeStructElement(FileStream stream, string objPrefix, string elementName, string fieldName, Type structType)
        {
            //write the struct begin
            this.fileWriteSerializeStructElementBegin(stream, elementName, fieldName);

            //intermediate variables
            string newPrefix = objPrefix + "." + fieldName;
            string fullyQTName = fullTypeName(structType);
            string shortTName = structType.Name;

            //write the serialization of attributes, recursivelly for parent types
            this.fileWriteSerializeTypeAttributes(stream, newPrefix, fullyQTName, shortTName, structType);

            //write the serialization of elements, recursivelly for parent types
            this.fileWriteSerializeTypeElements(stream, newPrefix, fullyQTName, shortTName, structType);

            //write the struct closing
            this.fileWriteSerializeStructElementClosing(stream);
        }

        private void fileWriteSerializeStructElementBegin(FileStream stream, string elementName, string fieldName)
        {
            string template =
@"
            // Field name: {0} (Struct Begin)
            this.writeStructElementBegin(xmlW, ""{1}"");
            {
";
            string data = template;
            data = data.Replace("{0}", fieldName);
            data = data.Replace("{1}", elementName);
            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeStructElementClosing(FileStream stream)
        {
            string template =
@"
                //[Struct End]
                this.writeStructElementEnd(xmlW);
            }
";
            this.fileWriteString(stream, template);
        }

        #endregion

        private void fileWriteSerializeListElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [List]
            this.writeSerializableListElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeIntElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Int]
            this.writeIntElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeByteElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Byte]
            this.writeByteElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeFloatElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Float]
            this.writeFloatElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeBoolElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Bool]
            this.writeBoolElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeStringElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [String]
            this.writeStringElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeEnumElementField(FileStream stream, string objPrefix, string attributeName, string fieldName, string enumTypeName)
        {
            string template =
@"
            // Field name: {2} [Enum]
            this.writeEnumElement(xmlW, ""{1}"", typeof({3}), {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);
            data = data.Replace("{3}", enumTypeName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeClassElementField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Class]
            this.writeClassElement(xmlW, ""{1}"", {0}.{2});
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteSerializeTypeElementsOpen(FileStream stream, Type type)
        {
            string template =
@"
            //
            // Elements for {0}
            //
";
            string data = template;
            data = data.Replace("{0}", type.Name);
            this.fileWriteString(stream, data);
        }

        #endregion

        #endregion

        #region "Deserialize"

        /// <summary>
        /// Write the opening of the Serialize Inner method
        /// </summary>
        private void fileWriteDeserializeInnerOpen(FileStream stream, string fullyQTName)
        {
            string template =
@"
        /// <summary>
        /// Deserializes the inner part
        /// </summary>
        public override void DeserializeInner(System.Xml.XmlReader xmlR, object targetObj)
        {
            //cast to concrete type
            {0} objS = targetObj as {0};
";

            string data = template;
            data = data.Replace("{0}", fullyQTName);

            this.fileWriteString(stream, data);
        }

        /// <summary>
        /// Write the closing of the Serialize Inner method
        /// </summary>
        private void fileWriteDeserializeInnerClosing(FileStream stream)
        {
            string template =
@"
        }
";
            string data = template;
            this.fileWriteString(stream, data);
        }

        #region "Write DeserializeInner Attributes"

        private void fileWriteDeserializeTypeAttributes(bool topmost, FileStream stream, string objPrefix, string fullyQTName, string shortTName, Type type)
        {
            //recursive call for base type
            if (type.BaseType != typeof(object))
            {
                this.fileWriteDeserializeTypeAttributes(false, stream, objPrefix, fullyQTName, shortTName, type.BaseType);
            }

            //variables for [XmlText()] attribute
            PropertyInfo piTextProp = null;
            FieldInfo piTextField = null;

            //detect the members that require serialization
            List<FieldInfo> serializableFields = new List<FieldInfo>();
            List<XmlAttributeAttribute> serializableAttributes = new List<XmlAttributeAttribute>();
            FieldInfo[] fields = type.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo currMember = fields[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableFields.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlAttributeAttribute);
                    }

                    object[] textAttributeAtt = currMember.GetCustomAttributes(typeof(XmlTextAttribute), false);
                    if (textAttributeAtt.Length > 0)
                    {
                        piTextField = currMember;
                    }
                }
            }

            List<PropertyInfo> serializableProperties = new List<PropertyInfo>();
            PropertyInfo[] properties = type.GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {
                PropertyInfo currMember = properties[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableProperties.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlAttributeAttribute);
                    }

                    object[] textAttributeAtt = currMember.GetCustomAttributes(typeof(XmlTextAttribute), false);
                    if (textAttributeAtt.Length > 0)
                    {
                        piTextProp = currMember;
                    }
                }
            }

            if ((serializableFields.Count > 0) || (serializableProperties.Count > 0)
                || (piTextProp != null) || (piTextField != null))
            {
                //write header
                this.fileWriteDeserializeTypeAttributesOpen(stream, type);

                if (serializableFields.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableFields.Count; i++)
                    {
                        FieldInfo currField = serializableFields[i];
                        XmlAttributeAttribute currAtt = serializableAttributes[i];

                        if (currField.FieldType == typeof(int))
                        {
                            this.fileWriteDeserializeIntAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(float))
                        {
                            this.fileWriteDeserializeFloatAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(string))
                        {
                            this.fileWriteDeserializeStringAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(bool))
                        {
                            this.fileWriteDeserializeBoolAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType == typeof(byte))
                        {
                            this.fileWriteDeserializeByteAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.FieldType.IsEnum)
                        {
                            this.fileWriteDeserializeEnumAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name, fullTypeName(currField.FieldType));
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }

                if (serializableProperties.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableProperties.Count; i++)
                    {
                        PropertyInfo currField = serializableProperties[i];
                        XmlAttributeAttribute currAtt = serializableAttributes[serializableFields.Count + i];

                        if (currField.PropertyType == typeof(int))
                        {
                            this.fileWriteDeserializeIntAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(float))
                        {
                            this.fileWriteDeserializeFloatAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(string))
                        {
                            this.fileWriteDeserializeStringAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(bool))
                        {
                            this.fileWriteDeserializeBoolAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(byte))
                        {
                            this.fileWriteDeserializeByteAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name);
                        }
                        else if (currField.PropertyType.IsEnum)
                        {
                            this.fileWriteDeserializeEnumAttributeField(stream, objPrefix, currAtt.AttributeName, currField.Name, fullTypeName(currField.PropertyType));
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }

                //write field that match as "XmlText"
                if (piTextField != null)
                {
                    this.fileWriteDeserializeStringAttributeField(stream, objPrefix, "text", piTextField.Name);
                }

                //write property that match as "XmlText"
                if (piTextProp != null)
                {
                    this.fileWriteDeserializeStringAttributeField(stream, objPrefix, "text", piTextProp.Name);
                }

            }

            //before exiting the topmost ouput the attribute block end
            if (topmost)
            {
                fileWriteDeserializeAttributesEnd(stream);
            }

        }

        private void fileWriteDeserializeIntAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {1} [Int]
            {0}.{2} = this.readIntAttribute(xmlR, ""{1}"");
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeByteAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Byte]
            {0}.{2} = this.readByteAttribute(xmlR, ""{1}"");
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeFloatAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Float]
            {0}.{2} = this.readFloatAttribute(xmlR, ""{1}"");
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeBoolAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Bool]
            {0}.{2} = this.readBoolAttribute(xmlR, ""{1}"");
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeStringAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [String]
            {0}.{2} = this.readStringAttribute(xmlR, ""{1}"");
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeEnumAttributeField(FileStream stream, string objPrefix, string attributeName, string fieldName, string enumTypeName)
        {
            string template =
@"
            // Field name: {2} [Enum]
            {0}.{2} = ({3})this.readEnumAttribute(xmlR, ""{1}"", typeof({3}));
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", attributeName);
            data = data.Replace("{2}", fieldName);
            data = data.Replace("{3}", enumTypeName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeTypeAttributesOpen(FileStream stream, Type type)
        {
            string template =
@"
            //
            // Atributtes for {0}
            //
";
            string data = template;
            data = data.Replace("{0}", type.Name);
            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeAttributesEnd(FileStream stream)
        {
            string template =
@"
            // done with attributes
            this.readEndAttributes(xmlR);
";
            this.fileWriteString(stream, template);
        }

        #endregion

        #region "Write SerializeInner Elements"

        private void fileWriteDeserializeTypeElements(FileStream stream, string objPrefix, string fullyQTName, string shortTName, Type type)
        {
            //recursive call for base type
            if (type.BaseType != typeof(object))
            {
                this.fileWriteDeserializeTypeElements(stream, objPrefix, fullyQTName, shortTName, type.BaseType);
            }

            //detect the members that require serialization
            List<FieldInfo> serializableFields = new List<FieldInfo>();
            List<XmlElementAttribute> serializableAttributes = new List<XmlElementAttribute>();
            FieldInfo[] fields = type.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                FieldInfo currMember = fields[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlElementAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableFields.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlElementAttribute);
                    }
                }
            }

            List<PropertyInfo> serializableProperties = new List<PropertyInfo>();
            PropertyInfo[] properties = type.GetProperties();
            for (int i = 0; i < properties.Length; i++)
            {
                PropertyInfo currMember = properties[i];
                if (currMember.DeclaringType == type)
                {
                    object[] attributeAtt = currMember.GetCustomAttributes(typeof(XmlElementAttribute), false);
                    if (attributeAtt.Length > 0)
                    {
                        serializableProperties.Add(currMember);
                        serializableAttributes.Add(attributeAtt[0] as XmlElementAttribute);
                    }
                }
            }

            if ((serializableFields.Count > 0) || (serializableProperties.Count>0))
            {
                //write header
                this.fileWriteSerializeTypeElementsOpen(stream, type);

                if (serializableFields.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableFields.Count; i++)
                    {
                        FieldInfo currField = serializableFields[i];
                        XmlElementAttribute currAtt = serializableAttributes[i];

                        if (currField.FieldType.GetInterface("System.Collections.IList") != null)
                        {
                            this.fileWriteDeserializeListElementField(stream, objPrefix, currField.Name, fullTypeName(currField.FieldType));
                        }
                        else if (currField.FieldType == typeof(int))
                        {
                            this.fileWriteDeserializeIntElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.FieldType == typeof(float))
                        {
                            this.fileWriteDeserializeFloatElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.FieldType == typeof(string))
                        {
                            this.fileWriteDeserializeStringElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.FieldType == typeof(bool))
                        {
                            this.fileWriteDeserializeBoolElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.FieldType == typeof(byte))
                        {
                            this.fileWriteDeserializeByteElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.FieldType.IsEnum)
                        {
                            this.fileWriteDeserializeEnumElementField(stream, objPrefix, currField.Name, fullTypeName(currField.FieldType));
                        }
                        else if (currField.FieldType.IsValueType)
                        {
                            //struct!
                            this.fileWriteDeserializeStructElement(stream, objPrefix, currAtt.ElementName, currField.Name, currField.FieldType);
                        }
                        else if (currField.FieldType.IsClass)
                        {
                            this.fileWriteDeserializeClassElementField(stream, objPrefix, fullTypeName(currField.FieldType), currField.Name);
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }

                if (serializableProperties.Count > 0)
                {
                    //write members
                    for (int i = 0; i < serializableProperties.Count; i++)
                    {
                        PropertyInfo currField = serializableProperties[i];
                        XmlElementAttribute currAtt = serializableAttributes[serializableFields.Count + i];

                        if (currField.PropertyType.GetInterface("System.Collections.IList") != null)
                        {
                            this.fileWriteDeserializeListElementField(stream, objPrefix, currField.Name, fullTypeName(currField.PropertyType));
                        }
                        else if (currField.PropertyType == typeof(int))
                        {
                            this.fileWriteDeserializeIntElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(float))
                        {
                            this.fileWriteDeserializeFloatElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(string))
                        {
                            this.fileWriteDeserializeStringElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(bool))
                        {
                            this.fileWriteDeserializeBoolElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.PropertyType == typeof(byte))
                        {
                            this.fileWriteDeserializeByteElementField(stream, objPrefix, currField.Name);
                        }
                        else if (currField.PropertyType.IsEnum)
                        {
                            this.fileWriteDeserializeEnumElementField(stream, objPrefix, currField.Name, fullTypeName(currField.PropertyType));
                        }
                        else if (currField.PropertyType.IsValueType)
                        {
                            //struct!
                            this.fileWriteDeserializeStructElement(stream, objPrefix, currAtt.ElementName, currField.Name, currField.PropertyType);
                        }
                        else if (currField.PropertyType.IsClass)
                        {
                            this.fileWriteDeserializeClassElementField(stream, objPrefix, fullTypeName(currField.PropertyType), currField.Name);
                        }
                        else
                        {
                            throw new Exception("Invalid type!");
                        }
                    }
                }
            }

        }

        #region "Structs"

        private void fileWriteDeserializeStructElement(FileStream stream, string objPrefix, string elementName, string fieldName, Type structType)
        {
            //write the struct begin
            this.fileWriteDeserializeStructElementBegin(stream, fieldName);

            //intermediate variables
            string newPrefix = objPrefix + "." + fieldName;
            string fullyQTName = fullTypeName(structType);
            string shortTName = structType.Name;

            //write the serialization of attributes, recursivelly for parent types
            this.fileWriteDeserializeTypeAttributes(true, stream, newPrefix, fullyQTName, shortTName, structType);

            //write the serialization of elements, recursivelly for parent types
            this.fileWriteDeserializeTypeElements(stream, newPrefix, fullyQTName, shortTName, structType);

            //write the struct closing
            this.fileWriteDeserializeStructElementClosing(stream);
        }

        private void fileWriteDeserializeStructElementBegin(FileStream stream, string fieldName)
        {
            string template =
@"
            // Field name: {0} (Struct Begin)
            this.readStructElementBegin(xmlR);
            {
";
            string data = template;
            data = data.Replace("{0}", fieldName);
            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeStructElementClosing(FileStream stream)
        {
            string template =
@"
                //[Struct End]
                this.readStructElementEnd(xmlR);
            }
";
            this.fileWriteString(stream, template);
        }

        #endregion

        private void fileWriteDeserializeListElementField(FileStream stream, string objPrefix, string fieldName, string fullTypeName)
        {
            string template =
@"
            // Field name: {2} (LIST)
            {0}.{2} = new {1}();
            {0}.{2}.ReadXml(xmlR);
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fullTypeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeIntElementField(FileStream stream, string objPrefix, string fieldName)
        {
            string template =
@"
            // Field name: {1} [Int]
            {0}.{1} = this.readIntElement(xmlR);
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeByteElementField(FileStream stream, string objPrefix, string fieldName)
        {
            string template =
@"
            // Field name: {1} [Byte]
            {0}.{1} = this.readByteElement(xmlR);
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeFloatElementField(FileStream stream, string objPrefix, string fieldName)
        {
            string template =
@"
            // Field name: {1} [Float]
            {0}.{1} = this.readFloatElement(xmlR);
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeBoolElementField(FileStream stream, string objPrefix, string fieldName)
        {
            string template =
@"
            // Field name: {1} [Bool]
            {0}.{1} = this.readBoolElement(xmlR);
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeStringElementField(FileStream stream, string objPrefix, string fieldName)
        {
            string template =
@"
            // Field name: {1} [String]
            {0}.{1} = this.readStringElement(xmlR);
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeEnumElementField(FileStream stream, string objPrefix, string fieldName, string enumTypeName)
        {
            string template =
@"
            // Field name: {1} [Enum]
            {0}.{1} = ({2})this.readEnumElement(xmlR, typeof({2}));
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fieldName);
            data = data.Replace("{2}", enumTypeName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeClassElementField(FileStream stream, string objPrefix, string fullTypeName, string fieldName)
        {
            string template =
@"
            // Field name: {2} [Class]
            {0}.{2} = this.readClassElement(xmlR) as {1};
";
            string data = template;
            data = data.Replace("{0}", objPrefix);
            data = data.Replace("{1}", fullTypeName);
            data = data.Replace("{2}", fieldName);

            this.fileWriteString(stream, data);
        }

        private void fileWriteDeserializeTypeElementsOpen(FileStream stream, Type type)
        {
            string template =
@"
            //
            // Elements for {0}
            //
";
            string data = template;
            data = data.Replace("{0}", type.Name);
            this.fileWriteString(stream, data);
        }

        #endregion

        #endregion

        #region "Common parts"

        /// <summary>
        /// Write the usings header
        /// </summary>
        private void fileWriteUsings(Type type, FileStream stream)
        {
            string template =
@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tomahawk.Serialization;
using Tomahawk.Serialization.SerialBoost;
";
            this.fileWriteString(stream, template);
        }

        /// <summary>
        /// Write the namespace and class header
        /// </summary>
        private void fileWriteClassOpen(FileStream stream, string className)
        {
            string template =
@"
namespace BoostSerializers
{
    public class {0} : BoostSerializer
    {
";
            string data = template;
            data = data.Replace("{0}", className);

            this.fileWriteString(stream, data);
        }

        /// <summary>
        /// Write the TypeName, ShortTypeName, CreateInstance, Serialize methods
        /// </summary>
        private void fileWriteBasicMethods(FileStream stream, string fullyQTName, string shortTName)
        {
            string template =
@"
        /// <summary>
        /// Name of the type we can serialize (fully qualified)
        /// </summary>
        public override string TypeName
        {
            get { return ""{0}""; }
        }        
        
        /// <summary>
        /// ShortName of the type we can serialize
        /// </summary>
        public override string ShortTypeName
        {
            get { return ""{1}""; }
        }

        /// <summary>
        /// Return an instance of the serializable type
        /// </summary>
        public override object CreateInstance()
        {
            return new {0}();
        }

        /// <summary>
        /// Serializes an object using the XmlWriter specified
        /// </summary>
        public override void Serialize(object obj2Serialize, System.Xml.XmlWriter xmlW)
        {
            //open header
            this.writeOpenObject(xmlW);

            //serialize the inner part
            this.SerializeInner(obj2Serialize, xmlW);

            //close header
            this.writeCloseObject(xmlW);
        }
";
            string data = template;
            data = data.Replace("{0}", fullyQTName);
            data = data.Replace("{1}", shortTName);
            this.fileWriteString(stream, data);
        }

        /// <summary>
        /// Write the class and namespace ending brackets
        /// </summary>
        private void fileWriteClassClose(FileStream stream)
        {
            string template =
@"
    }
}
";
            this.fileWriteString(stream, template);
        }

        #endregion

        #endregion

        #region "Utils"

        private FileStream createOutputFile(Type type, string outputFolder)
        {
            string fileName = fullTypeName(type);
            fileName = fileName.Replace(".", "_") + ".cs";
            string filePath = System.IO.Path.Combine(outputFolder, fileName);
            FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
            return fs;
        }

        private void fileWriteString(FileStream stream, string sData)
        {
            byte[] stringData = Encoding.UTF8.GetBytes(sData);
            stream.Write(stringData, 0, stringData.Length);
        }

        private string fullTypeName(Type type)
        {
            string retVal = type.FullName;
            if (type.IsGenericType)
            {
                Type[] genArg = type.GetGenericArguments();
                string fullName = type.FullName;
                int sepPos = fullName.IndexOf("`1");
                string firstPart = fullName.Substring(0, sepPos);
                retVal = string.Format("{0}<{1}>", firstPart, fullTypeName(genArg[0]));
            }
            retVal = retVal.Replace("+", ".");
            return retVal;
        }

        #endregion

    }

}
